package ws

import (
	"context"
	"fmt"
	"github.com/gorilla/websocket"
	"net/http"
	"sync"
	"wzgames/pkg/actor/actorservice"
	"wzgames/pkg/utils/dateutil"
)

type ServerOption func(o *WebSocket)

type WebSocket struct {
	*ProcMessage
	sessMgr  ISessionManager
	acceptor IWebSocketAcceptor

	conf         *WsConfig
	capturePanic bool

	started bool
	closed  bool
	mu      sync.Mutex
}

func (ws *WebSocket) Start(ctx context.Context) error {
	ws.mu.Lock()
	defer ws.mu.Unlock()

	if ws.started {
		return fmt.Errorf("websocket already started")
	}
	ws.started = true

	ws.acceptor.Start()

	return nil
}

func (ws *WebSocket) Stop(ctx context.Context) error {
	ws.mu.Lock()
	defer ws.mu.Unlock()

	if ws.closed {
		return nil
	}
	ws.closed = true
	ws.started = false
	ws.acceptor.Stop()
	ws.sessMgr.CloseAllSession()

	return nil
}

func (ws *WebSocket) EnableCaptureIOPanic(v bool) {
	ws.mu.Lock()
	defer ws.mu.Unlock()
	ws.capturePanic = v
}

func (ws *WebSocket) CaptureIOPanic() bool {
	return ws.capturePanic
}

func (ws *WebSocket) AddSession(sess ISession) {
	ws.sessMgr.Add(sess)
}

func (ws *WebSocket) RemoveSession(sess ISession) {
	ws.ProcData(sess, SessionClosed{})
	ws.sessMgr.Remove(sess)
}

func (ws *WebSocket) GetSession(sessId int64) ISession {
	return ws.sessMgr.GetSession(sessId)
}

func (ws *WebSocket) Kick(sessId int64) {
	sess := ws.sessMgr.GetSession(sessId)
	if sess != nil {
		sess.Close()
	}
}

func (ws *WebSocket) Broadcast(msg interface{}) {
	ws.sessMgr.ForEachSession(func(sess ISession) bool {
		sess.Send(msg)
		return true
	})
}

func NewWebSocketServer(conf *WsConfig, opts ...ServerOption) *WebSocket {
	ws := &WebSocket{
		ProcMessage:  &ProcMessage{},
		conf:         conf,
		sessMgr:      NewSessionManager(dateutil.UnixSecs()),
		capturePanic: true,
	}
	for _, o := range opts {
		o(ws)
	}
	ws.acceptor = &wsAcceptor{
		addr: ws.conf.Addr,
		ws:   ws,
		upgrader: websocket.Upgrader{
			CheckOrigin: func(r *http.Request) bool {
				return true
			},
		},
	}
	return ws
}

func Transmitter(transmitter IMessageTransmitter) ServerOption {
	return func(o *WebSocket) { o.SetTransmitter(transmitter) }
}

func Receiver(recv IMessageReceive) ServerOption {
	return func(o *WebSocket) { o.SetReceiver(recv) }
}

func SendCallback(fn func(interface{})) ServerOption {
	return func(o *WebSocket) { o.SetSendCallback(fn) }
}

func ActorAttach(s actorservice.IService) ServerOption {
	return func(o *WebSocket) {
		s.Attach(o)
	}
}
